using System;
using System.IO;
using System.Net.Sockets;
using OracleInternal.Common;

namespace OracleInternal.Network
{
	internal class Packet
	{
		internal static readonly int NSPCHS_SZ = 64;

		internal static readonly int NSPFIF_SZ = 1;

		internal static readonly int NSPOVR_SZ = NSPCHS_SZ + NSPFIF_SZ + TNSPacketOffsets.NSPDADAT;

		internal static readonly int NSPSID_SZ = 16;

		internal static readonly int NSPFSID = 1;

		internal static readonly int NSPFRDS = 2;

		internal static readonly int NSPFRDR = 4;

		internal static readonly int NSPFSRN = 8;

		internal static readonly int NSPFPRB = 16;

		internal TNSPacketType m_type;

		internal int m_length;

		internal int m_flags;

		protected int m_dataLength;

		protected int m_dataOffset;

		internal int m_totalLength;

		internal byte[] m_dataBuffer;

		internal SessionContext m_sessionCtx;

		internal Packet(SessionContext sessCtx)
		{
			m_sessionCtx = sessCtx;
		}

		internal Packet(SessionContext sessCtx, int bufferSize)
			: this(sessCtx)
		{
			CreateBuffer(bufferSize, 0, 0, sessCtx);
		}

		internal Packet(SessionContext sessCtx, byte[] buf)
			: this(sessCtx)
		{
			m_totalLength = buf.Length - NSPSID_SZ;
			m_dataBuffer = buf;
			GetHeaderValues(buf, out m_length, out m_flags, out m_type, sessCtx);
		}

		internal Packet(SessionContext sessCtx, int bufferSize, int pktType, int pktFlags)
			: this(sessCtx)
		{
			CreateBuffer(bufferSize, pktType, pktFlags, sessCtx);
		}

		internal Packet(Packet pkt)
		{
			m_sessionCtx = pkt.m_sessionCtx;
			m_length = pkt.m_length;
			m_type = pkt.m_type;
			m_flags = pkt.m_flags;
			m_dataLength = pkt.m_dataLength;
			m_dataOffset = pkt.m_dataOffset;
			m_dataBuffer = pkt.m_dataBuffer;
		}

		internal void CreateBuffer(int pktSize, int pktType, int pktFlags, SessionContext SCtx)
		{
			m_dataBuffer = new byte[pktSize + NSPSID_SZ];
			if (pktType != 6)
			{
				m_totalLength = pktSize;
			}
			else
			{
				m_totalLength = pktSize - (NSPCHS_SZ + NSPFIF_SZ);
			}
			if (!SCtx.m_NSHandshakeComplete || SCtx.m_myversion < SessionContext.NSVSNLSD)
			{
				m_dataBuffer[TNSPacketOffsets.NSPHDLEN] = (byte)(m_totalLength / 256);
				m_dataBuffer[TNSPacketOffsets.NSPHDLEN + 1] = (byte)(m_totalLength % 256);
			}
			else
			{
				m_dataBuffer[TNSPacketOffsets.NSPHDLEN + 3] = (byte)((uint)m_totalLength & 0xFFu);
				m_dataBuffer[TNSPacketOffsets.NSPHDLEN + 2] = (byte)((uint)HelperClass.URShift(m_totalLength, 8) & 0xFFu);
				m_dataBuffer[TNSPacketOffsets.NSPHDLEN + 1] = (byte)((uint)HelperClass.URShift(m_totalLength, 16) & 0xFFu);
				m_dataBuffer[TNSPacketOffsets.NSPHDLEN] = (byte)((uint)HelperClass.URShift(m_totalLength, 24) & 0xFFu);
			}
			m_dataBuffer[TNSPacketOffsets.NSPHDTYP] = (byte)pktType;
			if (m_sessionCtx.m_SID != null)
			{
				pktFlags |= NSPFSID;
			}
			m_dataBuffer[TNSPacketOffsets.NSPHDFLGS] = (byte)pktFlags;
		}

		internal virtual void Send()
		{
			if (m_sessionCtx.m_SID == null)
			{
				m_sessionCtx.m_socketStream.Write(m_dataBuffer, 0, m_totalLength);
				if (ProviderConfig.m_bTraceLevelNetwork)
				{
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.BulkCopy, m_dataBuffer, 0, m_totalLength);
				}
				return;
			}
			Buffer.BlockCopy(m_dataBuffer, m_totalLength, m_sessionCtx.m_SID, 0, NSPSID_SZ);
			m_sessionCtx.m_socketStream.Write(m_dataBuffer, 0, m_totalLength + NSPSID_SZ);
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.BulkCopy, m_dataBuffer, 0, m_totalLength + NSPSID_SZ);
			}
		}

		internal virtual void Receive()
		{
			int num = 0;
			while (num < TNSPacketOffsets.NSPSIZHD)
			{
				try
				{
					if ((num += m_sessionCtx.m_socketStream.Read(m_dataBuffer, num, TNSPacketOffsets.NSPSIZHD - num)) <= 0)
					{
						if (num == 0)
						{
							throw new NetworkException(12537);
						}
						throw new NetworkException(12570);
					}
				}
				catch (IOException ex)
				{
					if (m_sessionCtx.m_SID == null)
					{
						throw ex;
					}
					Exception innerException = ex.InnerException;
					while (innerException != null && !(innerException is SocketException))
					{
						innerException = innerException.InnerException;
					}
					if (innerException == null || ((SocketException)innerException).ErrorCode != 10054)
					{
						throw ex;
					}
					ConnectionOption connectionOption = new AddressResolution(m_sessionCtx.m_reconAddr, m_sessionCtx.m_conops.SNOConfig).ResolveConnectionString();
					if (connectionOption == null)
					{
						throw ex;
					}
					m_sessionCtx.m_transportAdapter.Connect(connectionOption);
					m_sessionCtx.m_socketStream = m_sessionCtx.m_transportAdapter.GetStream();
				}
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, m_dataBuffer, 0, num);
			}
			GetHeaderValues(m_dataBuffer, out m_length, out m_flags, out m_type, m_sessionCtx);
			int num2 = num;
			while (num < m_length)
			{
				try
				{
					if ((num += m_sessionCtx.m_socketStream.Read(m_dataBuffer, num, m_length - num)) <= 0)
					{
						throw new NetworkException(12570);
					}
				}
				catch (IOException)
				{
				}
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, m_dataBuffer, num2, num - num2);
			}
		}

		internal static void GetHeaderValues(byte[] DataBuf, out int Length, out int Flags, out TNSPacketType Type, SessionContext SCtx)
		{
			if (!SCtx.m_NSHandshakeComplete || SCtx.m_myversion < SessionContext.NSVSNLSD)
			{
				Length = DataBuf[TNSPacketOffsets.NSPHDLEN] & 0xFF;
				Length <<= 8;
				Length |= DataBuf[TNSPacketOffsets.NSPHDLEN + 1] & 0xFF;
			}
			else
			{
				Length = DataBuf[TNSPacketOffsets.NSPHDLEN] & 0xFF;
				Length <<= 8;
				Length |= DataBuf[TNSPacketOffsets.NSPHDLEN + 1] & 0xFF;
				Length <<= 8;
				Length |= DataBuf[TNSPacketOffsets.NSPHDLEN + 2] & 0xFF;
				Length <<= 8;
				Length |= DataBuf[TNSPacketOffsets.NSPHDLEN + 3] & 0xFF;
			}
			Type = (TNSPacketType)DataBuf[TNSPacketOffsets.NSPHDTYP];
			Flags = DataBuf[TNSPacketOffsets.NSPHDFLGS];
		}

		internal byte[] ExtractData()
		{
			byte[] array = null;
			if (m_dataLength <= 0)
			{
				array = null;
			}
			else if (m_length > m_dataOffset)
			{
				array = new byte[m_dataLength];
				Buffer.BlockCopy(m_dataBuffer, m_dataOffset, array, 0, m_dataLength);
			}
			else
			{
				byte[] array2 = new byte[m_dataLength];
				if (m_sessionCtx.m_readerStream.Read(array2) < 0)
				{
					throw new NetworkException(12570);
				}
				array = array2;
			}
			return array;
		}
	}
}
